#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include <sys/resource.h>
#include <MarkupSTL.h>

#include <log.h>
#include <global.h>
#include <cgi_control.h>

using namespace std;
#define DEFAULT_MAX_REQ_HEAD_LEN     1024*10
#define DEFAULT_MAX_REQ_BODY_LEN     1024*1024*10


HTTP_SVR_NS_BEGIN
volatile int*   TGlobal::_module_close           = NULL;
volatile int    TGlobal::_stopped                =0;
int             TGlobal::_accesslog              = 0;
const char*     TGlobal::_progname               = "qzhttp proxy";
const char*     TGlobal::_version                = "2.4.0 date: 2009-03-28";
int             TGlobal::_background             = 1;
string          TGlobal::_config_file;
bool            TGlobal::_limitable              = false;
string          TGlobal::_cfg_limit;

char            TGlobal::_addr[64]               = "0.0.0.0";  
uint16_t        TGlobal::_port                   = 80;  
int             TGlobal::_backlog                = 256;
int             TGlobal::_maxpoller              = 1024;
int             TGlobal::_webtimeout             = 10;
int             TGlobal::_helpertimeout          = 5;
int             TGlobal::_cleancycle             = 1;
int             TGlobal::_usechunk               = 1;
bool            TGlobal::_gzip_enable            =false;
bool            TGlobal::_shutdown_write_enable  =false;
int             TGlobal::_gzip_inf               =0;
int             TGlobal::_gzip_level             =Z_BEST_COMPRESSION;
int             TGlobal::_gzip_thread_count      =4;
int             TGlobal::_max_req_head_lenth         =DEFAULT_MAX_REQ_HEAD_LEN;
int             TGlobal::_max_req_body_lenth          =DEFAULT_MAX_REQ_BODY_LEN;
bool			TGlobal::_status_log			 =false;
string			TGlobal::_FIFO_path				 ="";
DebugMem*		TGlobal::_debug_mem				 =NULL;

string          TGlobal::_server_ip; 
int             TGlobal::_server_port            =0;
string     		TGlobal::_old_server_ip;
int             TGlobal::_old_server_port        =0;
string          TGlobal::_server_key;

void TGlobal::ShowVersion ()
{
	printf("%s v%s\n", _progname, _version);
}

void TGlobal::ShowUsage ()
{
	ShowVersion();    
	printf("Usage:\n    %s [-d] [-f httpsvr.conf] [-x cgiconfig.xml]\n", _progname);
}



int TGlobal::DaemonInit (const char* conf_file)
{
    if (NULL == conf_file || strlen(conf_file) == 0)
    {
        fprintf(stderr, "module config file is invalid.\n");
        return -1;
    }

    _config_file = conf_file;

    CMarkupSTL  markup;
    if(!markup.Load(conf_file))
    {       
        fprintf(stderr, "Load xml %s failed\n", conf_file);
        return -1;
    }

    if(!markup.FindElem("SYSTEM"))
    {
        fprintf(stderr, "Can not FindElem [SYSTEM] in xml %s failed\n", conf_file);
        return -1;
    }

	
    string log_dir = markup.GetAttrib("LogDir");
    string log_num = markup.GetAttrib("LogNum");
    string log_size = markup.GetAttrib("LogSize");
    
	init_log("proxy", log_dir.c_str(), atoi(log_num.c_str()), atoi(log_size.c_str()));

    string log_level = markup.GetAttrib("LogLevel");
	
    set_log_level(atoi(log_level.c_str()));
    
    //get access log switch
    _accesslog = atoi(markup.GetAttrib("AccessLog").c_str());

    //get max poll
    _maxpoller = atoi(markup.GetAttrib("MaxFdCount").c_str());
    if(_maxpoller < 1024)
    {
        _maxpoller = 1024;
    }

	//malloc the debug memory
	_debug_mem=(DebugMem*)malloc(_maxpoller*(sizeof(DebugMem)+1));
	if(_debug_mem==NULL){
			log_boot ("malloc debug_mem failed");
			printf("malloc debug_mem failed");
			return -1;
	}else{
			memset(_debug_mem,0x00,_maxpoller*(sizeof(DebugMem)+1));
	}
    
    //get web time out
    _webtimeout = atoi(markup.GetAttrib("WebTimeout").c_str());
    if (_webtimeout < 1)
    {
        _webtimeout = 1;
    }
   log_boot ("_webtimeout is %d",_webtimeout);

   
   _helpertimeout = atoi(markup.GetAttrib("HelperTimeout").c_str());
    if (_helpertimeout < 1)
    {
        _helpertimeout = 1;
    }
   log_boot ("HelperTimeout is %d",_helpertimeout);
   
    
    //get listen backup
    _backlog = atoi(markup.GetAttrib("MaxListenCount").c_str());
    if (_backlog < 256)
    {
        _backlog = 256;
    }
    
    //get addr & port
    string addr = markup.GetAttrib("BindAddr");
    if (0 == addr.length())
    {
        log_boot ("bind ip address invalid.");
        return -1;
    }

    strncpy(_addr, addr.c_str(), sizeof(_addr) - 1);
    
    //_port = atoi(markup.GetAttrib("BindPort").c_str());

    _cleancycle = atoi(markup.GetAttrib("CleanCycle").c_str());
    
    string chunk = markup.GetAttrib("UseChunk");
    if(chunk.length() > 0)
    {
        _usechunk = atoi(chunk.c_str());
        if(_usechunk < 0)
            _usechunk = 1;
    }


    
    _limitable = (strcasecmp(markup.GetAttrib("AccessLimit").c_str(), "enable") == 0 ? true : false);
    
    _cfg_limit = markup.GetAttrib("LimitConfig");

    _gzip_enable              = (strcasecmp(markup.GetAttrib("Dynamic_GZip").c_str(),"enable")==0 ? true: false);
    _gzip_inf               = atoi(markup.GetAttrib("Dynamic_GZip_Inf").c_str()); 
   
    
    if(_gzip_enable==true)
    {
        if(_gzip_inf<0)
            _gzip_inf=0;
        log_boot("enable Gzip of CGI output,with inf =%d",_gzip_inf);
    }
    else
    {
        _gzip_inf=0;
        log_boot("disable Gzip of CGI output");
    }

   _shutdown_write_enable      = (strcasecmp(markup.GetAttrib("ShutDown_Write").c_str(),"enable")==0 ? true: false);
    
    _gzip_thread_count= atoi(markup.GetAttrib("GZipThreadCount").c_str()); 
  if(_gzip_thread_count<=0||_gzip_thread_count>4){
        _gzip_thread_count=4;
    }
    _gzip_level       = (strcasecmp(markup.GetAttrib("GZipLevel").c_str(),"best_speed")==0 ?  Z_BEST_SPEED: Z_BEST_COMPRESSION);

    _max_req_head_lenth=atoi(markup.GetAttrib("MaxReqHeadLen").c_str());
    _max_req_body_lenth=atoi(markup.GetAttrib("MaxReqBodyLen").c_str());

    if(_max_req_head_lenth<=0)
        _max_req_head_lenth=DEFAULT_MAX_REQ_HEAD_LEN;


    if(_max_req_body_lenth<=0)
        _max_req_body_lenth=DEFAULT_MAX_REQ_BODY_LEN;

	_status_log=(atoi(markup.GetAttrib("StatusLog").c_str())==1?true:false);
	_FIFO_path=markup.GetAttrib("FIFOPath");

	//todo create FIFO here
	if(_status_log){
			if(_FIFO_path.size()<0){
					fprintf(stderr,"%s","the status log is enable,but the FIFO path is NULL");
					return -1;
			}
			if((mkfifo(_FIFO_path.c_str(),O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)){
					fprintf(stderr,"creat FIFO file %s error!!",_FIFO_path.c_str());
					return -1;
			}
	}

     
    log_boot("Max req head lenth is %d,max total req lenth is %d",_max_req_head_lenth,_max_req_body_lenth);
    return 0;
}

void TGlobal::DaemonStart(void)
{   
/*
    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = update_handler;
    sigaction(SIGUSR2, &sa, NULL);
*/

	struct sigaction sa;
	sigset_t sset;
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = update_handler;
	sigaction(SIGINT, &sa, NULL);
	sigaction(SIGTERM, &sa, NULL);
	sigaction(SIGQUIT, &sa, NULL);
	sigaction(SIGHUP, &sa, NULL);
	signal(SIGPIPE,SIG_IGN); 
	signal(SIGCHLD,SIG_IGN); 

	sigemptyset(&sset);
	sigaddset(&sset, SIGSEGV);
	sigaddset(&sset, SIGBUS);
	sigaddset(&sset, SIGABRT);
	sigaddset(&sset, SIGILL);
	sigaddset(&sset, SIGCHLD);
	sigaddset(&sset, SIGFPE);
	sigprocmask(SIG_UNBLOCK, &sset, &sset);

	daemon (1, 1);

}   

void TGlobal::update_handler(int signo)
{
    log_boot("*** ATTENTION: module [proxy] receive reload signal updating ***");

    if(TCGIControl::Instance()->open(TGlobal::_config_file.c_str()) < 0)
    {
        log_boot ("reload config file for cgi control failed.");
    }
}

string TGlobal::trim(string &s)
{
    const string &space = HTTP_SPACE_CHAR;
    
    string r=s.erase(s.find_last_not_of(space)+1);
    
    return r.erase(0,r.find_first_not_of(space));
}

string TGlobal::ltrim(string &s)
{
    const string &space = HTTP_SPACE_CHAR;
    
    return s.erase(0,s.find_first_not_of(space));
}

string TGlobal::rtrim(string &s)
{
    const string &space = HTTP_SPACE_CHAR;
    
    return s.erase(s.find_last_not_of(space)+1);
}

HTTP_SVR_NS_END
